package com.aisino.nmg.mybatis.generator;

import com.aisino.nmg.config.GeneratorProperties;
import com.aisino.nmg.system.generator.mapper.AppConfigMapper;
import com.aisino.nmg.system.generator.mapper.ColumnConfigMapper;
import com.aisino.nmg.system.generator.mapper.TableConfigMapper;
import com.aisino.nmg.system.generator.model.ColumnConfig;
import com.aisino.nmg.system.generator.model.TableConfig;
import com.aisino.nmg.util.SpringContextUtil;
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.codegen.AbstractGenerator;
import org.mybatis.generator.codegen.AbstractJavaClientGenerator;
import org.mybatis.generator.codegen.AbstractJavaGenerator;
import org.mybatis.generator.codegen.AbstractXmlGenerator;
import org.mybatis.generator.codegen.mybatis3.javamapper.AnnotatedClientGenerator;
import org.mybatis.generator.codegen.mybatis3.javamapper.JavaMapperGenerator;
import org.mybatis.generator.codegen.mybatis3.javamapper.MixedClientGenerator;
import org.mybatis.generator.codegen.mybatis3.model.BaseRecordGenerator;
import org.mybatis.generator.codegen.mybatis3.model.ExampleGenerator;
import org.mybatis.generator.codegen.mybatis3.model.PrimaryKeyGenerator;
import org.mybatis.generator.codegen.mybatis3.model.RecordWithBLOBsGenerator;
import org.mybatis.generator.codegen.mybatis3.xmlmapper.XMLMapperGenerator;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.ObjectFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class CustomIntrospectedTableMyBatis3Impl extends IntrospectedTable {
	private AppConfigMapper appConfigMapper;

	private TableConfigMapper tableConfigMapper;

	private ColumnConfigMapper columnConfigMapper;

	protected List<AbstractJavaGenerator> javaModelGenerators;

	protected List<AbstractJavaGenerator> clientGenerators;

	protected AbstractXmlGenerator xmlMapperGenerator;

	public CustomIntrospectedTableMyBatis3Impl() {
		super(TargetRuntime.MYBATIS3);
		javaModelGenerators = new ArrayList<AbstractJavaGenerator>();
		clientGenerators = new ArrayList<AbstractJavaGenerator>();
		appConfigMapper = SpringContextUtil.getBean(AppConfigMapper.class);
		tableConfigMapper = SpringContextUtil.getBean(TableConfigMapper.class);
		columnConfigMapper = SpringContextUtil.getBean(ColumnConfigMapper.class);
	}

	@Override
	public void calculateGenerators(List<String> warnings,
	                                ProgressCallback progressCallback) {
		calculateJavaModelGenerators(warnings, progressCallback);

		AbstractJavaClientGenerator javaClientGenerator =
				calculateClientGenerators(warnings, progressCallback);
		IntrospectedTable introspectedTable = javaClientGenerator.getIntrospectedTable();
		// TODO 拓展点 进行存库操作
		GeneratorProperties generatorProperties = SpringContextUtil.getBean(GeneratorProperties.class);
		TableConfigMapper tableConfigMapper = SpringContextUtil.getBean(TableConfigMapper.class);
		ColumnConfigMapper columnConfigMapper = SpringContextUtil.getBean(ColumnConfigMapper.class);

		TableConfig tableConfig = new TableConfig();
		tableConfig.setTableName(introspectedTable.getTableConfiguration().getTableName());
		tableConfig.setDomainObjectName(introspectedTable.getTableConfiguration().getDomainObjectName());
		tableConfig.setRemarks(introspectedTable.getRemarks());
		tableConfig.setAppConfigId(generatorProperties.getAppConfigId());
		tableConfigMapper.insert(tableConfig);

		// 表的主键列集合
		List<IntrospectedColumn> introspectedColumns = introspectedTable.getPrimaryKeyColumns();
		Iterator<IntrospectedColumn> it = introspectedColumns.iterator();
		List<ColumnConfig> columnConfigs = new ArrayList<>();
		ColumnConfig columnConfig = null;
		while(it.hasNext()){
			columnConfig = new ColumnConfig();
			IntrospectedColumn introspectedColumn = it.next();
			columnConfig.setActualColumnName(introspectedColumn.getActualColumnName());
			columnConfig.setJdbcType(introspectedColumn.getJdbcTypeName());
			columnConfig.setIsNotNull(introspectedColumn.isNullable() ? "1" : "0");
			columnConfig.setColumnLength(String.valueOf(introspectedColumn.getLength()));
			columnConfig.setJavaPropertyName(introspectedColumn.getJavaProperty());
			columnConfig.setJavaShortType(introspectedColumn.getFullyQualifiedJavaType().getShortName());
			columnConfig.setJavaFullType(introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName());
			columnConfig.setIsPrimaryKey("1");
			columnConfig.setRemarks(introspectedColumn.getRemarks());
			columnConfig.setTableConfigId(tableConfig.getId());

			columnConfigs.add(columnConfig);
		}

		// 表的普通列集合
		introspectedColumns = introspectedTable.getBaseColumns();
		it = introspectedColumns.iterator();
		while(it.hasNext()){
			columnConfig = new ColumnConfig();
			IntrospectedColumn introspectedColumn = it.next();
			columnConfig.setActualColumnName(introspectedColumn.getActualColumnName());
			columnConfig.setJdbcType(introspectedColumn.getJdbcTypeName());
			columnConfig.setIsNotNull(introspectedColumn.isNullable() ? "1" : "0");
			columnConfig.setColumnLength(String.valueOf(introspectedColumn.getLength()));
			columnConfig.setJavaPropertyName(introspectedColumn.getJavaProperty());
			columnConfig.setJavaShortType(introspectedColumn.getFullyQualifiedJavaType().getShortName());
			columnConfig.setJavaFullType(introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName());
			columnConfig.setIsPrimaryKey("0");
			columnConfig.setRemarks(introspectedColumn.getRemarks());
			columnConfig.setTableConfigId(tableConfig.getId());

			columnConfigs.add(columnConfig);
		}

		// 表的大字段列集合
		introspectedColumns = introspectedTable.getBLOBColumns();
		it = introspectedColumns.iterator();
		while(it.hasNext()){
			columnConfig = new ColumnConfig();
			IntrospectedColumn introspectedColumn = it.next();
			columnConfig.setActualColumnName(introspectedColumn.getActualColumnName());
			columnConfig.setJdbcType(introspectedColumn.getJdbcTypeName());
			columnConfig.setIsNotNull(introspectedColumn.isNullable() ? "1" : "0");
			columnConfig.setColumnLength(String.valueOf(introspectedColumn.getLength()));
			columnConfig.setJavaPropertyName(introspectedColumn.getJavaProperty());
			columnConfig.setJavaShortType(introspectedColumn.getFullyQualifiedJavaType().getShortName());
			columnConfig.setJavaFullType(introspectedColumn.getFullyQualifiedJavaType().getFullyQualifiedName());
			columnConfig.setIsPrimaryKey("0");
			columnConfig.setRemarks(introspectedColumn.getRemarks());
			columnConfig.setTableConfigId(tableConfig.getId());

			columnConfigs.add(columnConfig);
		}

		columnConfigMapper.insertBatch(columnConfigs);

		calculateXmlMapperGenerator(javaClientGenerator, warnings, progressCallback);
	}

	protected void calculateXmlMapperGenerator(AbstractJavaClientGenerator javaClientGenerator,
	                                           List<String> warnings,
	                                           ProgressCallback progressCallback) {
		if (javaClientGenerator == null) {
			if (context.getSqlMapGeneratorConfiguration() != null) {
				xmlMapperGenerator = new XMLMapperGenerator();
			}
		} else {
			xmlMapperGenerator = javaClientGenerator.getMatchedXMLGenerator();
		}

		initializeAbstractGenerator(xmlMapperGenerator, warnings,
		                            progressCallback);
	}

	protected AbstractJavaClientGenerator calculateClientGenerators(List<String> warnings,
	                                                                ProgressCallback progressCallback) {
		if (!rules.generateJavaClient()) {
			return null;
		}

		AbstractJavaClientGenerator javaGenerator = createJavaClientGenerator();
		if (javaGenerator == null) {
			return null;
		}

		initializeAbstractGenerator(javaGenerator, warnings, progressCallback);
		clientGenerators.add(javaGenerator);

		return javaGenerator;
	}

	protected AbstractJavaClientGenerator createJavaClientGenerator() {
		if (context.getJavaClientGeneratorConfiguration() == null) {
			return null;
		}

		String type = context.getJavaClientGeneratorConfiguration()
		                     .getConfigurationType();

		AbstractJavaClientGenerator javaGenerator;
		if ("XMLMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
			javaGenerator = new JavaMapperGenerator();
		} else if ("MIXEDMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
			javaGenerator = new MixedClientGenerator();
		} else if ("ANNOTATEDMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
			javaGenerator = new AnnotatedClientGenerator();
		} else if ("MAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
			javaGenerator = new JavaMapperGenerator();
		} else {
			javaGenerator = (AbstractJavaClientGenerator) ObjectFactory
					.createInternalObject(type);
		}

		return javaGenerator;
	}

	protected void calculateJavaModelGenerators(List<String> warnings,
	                                            ProgressCallback progressCallback) {
		if (getRules().generateExampleClass()) {
			AbstractJavaGenerator javaGenerator = new ExampleGenerator();
			initializeAbstractGenerator(javaGenerator, warnings,
			                            progressCallback);
			javaModelGenerators.add(javaGenerator);
		}

		if (getRules().generatePrimaryKeyClass()) {
			AbstractJavaGenerator javaGenerator = new PrimaryKeyGenerator();
			initializeAbstractGenerator(javaGenerator, warnings,
			                            progressCallback);
			javaModelGenerators.add(javaGenerator);
		}

		if (getRules().generateBaseRecordClass()) {
			AbstractJavaGenerator javaGenerator = new BaseRecordGenerator();
			initializeAbstractGenerator(javaGenerator, warnings,
			                            progressCallback);
			javaGenerator.getIntrospectedTable().getFullyQualifiedTable();
			javaModelGenerators.add(javaGenerator);
		}

		if (getRules().generateRecordWithBLOBsClass()) {
			AbstractJavaGenerator javaGenerator = new RecordWithBLOBsGenerator();
			initializeAbstractGenerator(javaGenerator, warnings,
			                            progressCallback);
			javaModelGenerators.add(javaGenerator);
		}
	}

	protected void initializeAbstractGenerator(
			AbstractGenerator abstractGenerator, List<String> warnings,
			ProgressCallback progressCallback) {
		if (abstractGenerator == null) {
			return;
		}

		abstractGenerator.setContext(context);
		abstractGenerator.setIntrospectedTable(this);
		abstractGenerator.setProgressCallback(progressCallback);
		abstractGenerator.setWarnings(warnings);
	}

	@Override
	public List<GeneratedJavaFile> getGeneratedJavaFiles() {
		List<GeneratedJavaFile> answer = new ArrayList<GeneratedJavaFile>();

		for (AbstractJavaGenerator javaGenerator : javaModelGenerators) {
			List<CompilationUnit> compilationUnits = javaGenerator
					.getCompilationUnits();
			for (CompilationUnit compilationUnit : compilationUnits) {
				GeneratedJavaFile gjf = new GeneratedJavaFile(compilationUnit,
				                                              context.getJavaModelGeneratorConfiguration()
				                                                     .getTargetProject(),
				                                              context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
				                                              context.getJavaFormatter());
				answer.add(gjf);
			}
		}

		for (AbstractJavaGenerator javaGenerator : clientGenerators) {
			List<CompilationUnit> compilationUnits = javaGenerator
					.getCompilationUnits();
			for (CompilationUnit compilationUnit : compilationUnits) {
				GeneratedJavaFile gjf = new GeneratedJavaFile(compilationUnit,
				                                              context.getJavaClientGeneratorConfiguration()
				                                                     .getTargetProject(),
				                                              context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
				                                              context.getJavaFormatter());
				answer.add(gjf);
			}
		}

		return answer;
	}

	@Override
	public List<GeneratedXmlFile> getGeneratedXmlFiles() {
		List<GeneratedXmlFile> answer = new ArrayList<GeneratedXmlFile>();

		if (xmlMapperGenerator != null) {
			Document document = xmlMapperGenerator.getDocument();
			GeneratedXmlFile gxf = new GeneratedXmlFile(document,
			                                            getMyBatis3XmlMapperFileName(), getMyBatis3XmlMapperPackage(),
			                                            context.getSqlMapGeneratorConfiguration().getTargetProject(),
			                                            true, context.getXmlFormatter());
			if (context.getPlugins().sqlMapGenerated(gxf, this)) {
				answer.add(gxf);
			}
		}

		return answer;
	}

	@Override
	public int getGenerationSteps() {
		return javaModelGenerators.size()
		       + clientGenerators.size()
		       + (xmlMapperGenerator == null ? 0 : 1);
	}

	@Override
	public boolean isJava5Targeted() {
		return true;
	}

	@Override
	public boolean requiresXMLGenerator() {
		AbstractJavaClientGenerator javaClientGenerator =
				createJavaClientGenerator();

		if (javaClientGenerator == null) {
			return false;
		} else {
			return javaClientGenerator.requiresXMLGenerator();
		}
	}
}
